// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "zencore.h"

#include <stdint.h>
#include <zenbase/concepts.h>

//////////////////////////////////////////////////////////////////////////
// UE Numeric constants

#define MIN_uint8  ((uint8_t)0x00)
#define MIN_uint16 ((uint16_t)0x0000)
#define MIN_uint32 ((uint32_t)0x00000000)
#define MIN_uint64 ((uint64_t)0x0000000000000000)
#define MIN_int8   ((int8_t)-128)
#define MIN_int16  ((int16_t)-32768)
#define MIN_int32  ((int32_t)0x80000000)
#define MIN_int64  ((int64_t)0x8000000000000000)

#define MAX_uint8  ((uint8_t)0xff)
#define MAX_uint16 ((uint16_t)0xffff)
#define MAX_uint32 ((uint32_t)0xffffffff)
#define MAX_uint64 ((uint64_t)0xffffffffffffffff)
#define MAX_int8   ((int8_t)0x7f)
#define MAX_int16  ((int16_t)0x7fff)
#define MAX_int32  ((int32_t)0x7fffffff)
#define MAX_int64  ((int64_t)0x7fffffffffffffff)

#define MIN_flt (1.175494351e-38F) /* min positive value */
#define MAX_flt (3.402823466e+38F)
#define MIN_dbl (2.2250738585072014e-308) /* min positive value */
#define MAX_dbl (1.7976931348623158e+308)

//////////////////////////////////////////////////////////////////////////

#if ZEN_COMPILER_MSC || ZEN_PLATFORM_WINDOWS
#	pragma intrinsic(_BitScanReverse)
#	pragma intrinsic(_BitScanReverse64)
#else
inline uint8_t
_BitScanReverse(unsigned long* Index, uint32_t Mask)
{
	if (Mask == 0)
	{
		return 0;
	}

	*Index = 31 - __builtin_clz(Mask);
	return 1;
}

inline uint8_t
_BitScanReverse64(unsigned long* Index, uint64_t Mask)
{
	if (Mask == 0)
	{
		return 0;
	}

	*Index = 63 - __builtin_clzll(Mask);
	return 1;
}

inline uint8_t
_BitScanForward64(unsigned long* Index, uint64_t Mask)
{
	if (Mask == 0)
	{
		return 0;
	}

	*Index = __builtin_ctzll(Mask);
	return 1;
}
#endif

namespace zen {

inline constexpr bool
IsPow2(uint64_t n)
{
	return 0 == (n & (n - 1));
}

/// Round an integer up to the closest integer multiplier of 'base' ('base' must be a power of two)
template<Integral T>
T
RoundUp(T Value, auto Base)
{
	ZEN_ASSERT_SLOW(IsPow2(Base));
	return ((Value + T(Base - 1)) & (~T(Base - 1)));
}

bool
IsMultipleOf(Integral auto Value, auto MultiplierPow2)
{
	ZEN_ASSERT_SLOW(IsPow2(MultiplierPow2));
	return (Value & (MultiplierPow2 - 1)) == 0;
}

inline uint64_t
NextPow2(uint64_t n)
{
	// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2

	--n;

	n |= n >> 1;
	n |= n >> 2;
	n |= n >> 4;
	n |= n >> 8;
	n |= n >> 16;
	n |= n >> 32;

	return n + 1;
}

static inline uint32_t
FloorLog2(uint32_t Value)
{
	// Use BSR to return the log2 of the integer
	unsigned long Log2;
	if (_BitScanReverse(&Log2, Value) != 0)
	{
		return Log2;
	}

	return 0;
}

static inline uint32_t
CountLeadingZeros(uint32_t Value)
{
	unsigned long Log2 = 0;
	_BitScanReverse64(&Log2, (uint64_t(Value) << 1) | 1);
	return 32 - Log2;
}

static inline uint64_t
FloorLog2_64(uint64_t Value)
{
	unsigned long Log2 = 0;
	long		  Mask = -long(_BitScanReverse64(&Log2, Value) != 0);
	return Log2 & Mask;
}

static inline uint64_t
CountLeadingZeros64(uint64_t Value)
{
	unsigned long Log2 = 0;
	long		  Mask = -long(_BitScanReverse64(&Log2, Value) != 0);
	return ((63 - Log2) & Mask) | (64 & ~Mask);
}

static inline uint64_t
CeilLogTwo64(uint64_t Arg)
{
	int64_t Bitmask = ((int64_t)(CountLeadingZeros64(Arg) << 57)) >> 63;
	return (64 - CountLeadingZeros64(Arg - 1)) & (~Bitmask);
}

static inline uint64_t
CountTrailingZeros64(uint64_t Value)
{
	if (Value == 0)
	{
		return 64;
	}
	unsigned long BitIndex;				  // 0-based, where the LSB is 0 and MSB is 31
	_BitScanForward64(&BitIndex, Value);  // Scans from LSB to MSB
	return BitIndex;
}

//////////////////////////////////////////////////////////////////////////

static inline bool
IsPointerAligned(const void* Ptr, uint64_t Alignment)
{
	ZEN_ASSERT_SLOW(IsPow2(Alignment));

	return 0 == (reinterpret_cast<uintptr_t>(Ptr) & (Alignment - 1));
}

//////////////////////////////////////////////////////////////////////////

#if ZEN_PLATFORM_WINDOWS
#	ifdef min
#		error "Looks like you did #include <windows.h> -- use <zencore/windows.h> instead"
#	endif
#endif

constexpr auto
Min(auto x, auto y)
{
	return x < y ? x : y;
}

constexpr auto
Max(auto x, auto y)
{
	return x > y ? x : y;
}

//////////////////////////////////////////////////////////////////////////

void intmath_forcelink();  // internal

}  // namespace zen
